﻿namespace Busconeitor.Domain.Entities
{
    using System;
    using Utils.SimpleList;

    /// <summary>
    /// Representa una palabra dentro del Vocabulario.
    /// </summary>
    public class TerminoEntity : BaseEntity, IComparable
    {
        #region Atributos

        /// <summary>
        /// Tamaño Minimo de la palabra 
        /// </summary>
        public static readonly int MinWordLength = 2;

        /// <summary>
        /// Tamaño Maximo de la palabra
        /// </summary>
        public static readonly int MaxWordLength = 21;

        /// <summary>
        /// Palabra sin los signos de puntuación
        /// </summary>
        private string _cleanTerm;

        /// <summary>
        /// Máxima frecuencia de aparicion del termino
        /// </summary>
        private int _tfMax;

        /// <summary>
        /// Frecuencia de aparicion del termino en el documento actual
        /// </summary>
        private int _tfDoc;

        /// <summary>
        /// Cantidad de documentos en los que aparece el término
        /// </summary>
        private int _nr;

        /// <summary>
        /// Palabra con signos de puntuacion del término
        /// </summary>
        private SimpleList _ps;

        #endregion

        #region Constructores

        /// <summary>
        /// Constructor por defecto 
        /// </summary>
        /// <param name="pIdTermino"></param>
        public TerminoEntity(int pIdTermino)
        {
            Id = pIdTermino;
        }

        /// <summary>
        /// Constructor con parámetros 
        /// </summary>
        /// <param name="pIdTerm">id del término</param>
        /// <param name="pTf">frecuencia del término por doc</param>
        /// <param name="pNr">cantidad de documentos en los q aparece el término</param>
        public TerminoEntity(int pIdTerm, int pNr, int pTf)
        {
            _nr = pNr;
            Id = pIdTerm;
            _tfDoc = pTf;
        }

        public TerminoEntity(int pIdTerm, int pTf, int pNr, int maxTf)
        {
            _nr = pNr;
            Id = pIdTerm;
            _tfDoc = pTf;
            _tfMax = maxTf;
        }

        /// <summary>
        /// Constructor con parámetros de la clase Palabra que inicializa sus atributos. 
        /// </summary>
        /// <param name="pIdTerm">identificador de la palabra.</param>
        /// <param name="cleanTerm">palabra sin caracteres raros o signos de puntuacion.</param>
        public TerminoEntity(int pIdTerm, string cleanTerm)
        {
            _cleanTerm = cleanTerm;
            Id = pIdTerm;
            _ps = new SimpleList();
        }

        /// <summary>
        /// Constructor con parametros de la clase Palabra que inicializa sus atributos.
        /// </summary>
        /// <param name="cleanTerm">palabra sin caracteres raros o signos de puntuacion.</param>
        /// <param name="dirtyTerm">palabra con signos de puntuacion.</param>
        /// <param name="pIndex">indice de ubicacion de la palabra dentro del documento.</param>
        public TerminoEntity(string cleanTerm, string dirtyTerm, int pIndex)
        {
            _cleanTerm = cleanTerm;
            _ps = new SimpleList();

            if (pIndex > 0)
            {
                _ps.AddFirst(new TerminoSucioEntity(pIndex, dirtyTerm));
            }
        }

        #endregion

        #region Propiedades

        /// <summary>
        /// Lista simple que contiene todas las palabras con signos de puntuacion asociadas a la palabra limpia actual.
        /// </summary>
        public SimpleList Ps
        {
            get { return _ps; }
            set { _ps = value; }
        }

        /// <summary>
        /// Palabra limpia sin signos de puntuacion.
        /// </summary>
        public string CleanTerm
        {
            get { return _cleanTerm; }
            set { _cleanTerm = value; }
        }

        /// <summary>
        /// Frecuencia del termino dentro de un documento en proceso.
        /// </summary>
        public int TfDoc
        {
            get { return _tfDoc; }
            set { _tfDoc = value; }
        }

        /// <summary>
        /// Cantidad de documentos en los que la palabra aparecio.
        /// </summary>
        public int Nr
        {
            get { return _nr; }
            set { _nr = value; }
        }

        /// <summary>
        /// Maxima frecuencia de aparicion del termino en los documentos.
        /// </summary>
        public int TfMax
        {
            get { return _tfMax; }
            set { _tfMax = value; }
        }

        #endregion
        
        #region Metodos

        /// <summary>
        /// Agega un nuevo termino con signos de puntuacion asociado a la palabra limpia actual.
        /// </summary>
        /// <param name="dirtyTerm">palabra con signos de puntuacion.</param>
        /// <param name="pIndex">indice de ubicacion de la palabra dentro del documento.</param>
        public void AgregarTerminoVariado(string dirtyTerm, int pIndex)
        {
            if (pIndex > 0)
            {
                var term = (TerminoSucioEntity)_ps.Search(new TerminoSucioEntity(dirtyTerm));

                if (term == null)
                {
                    _ps.AddFirst(new TerminoSucioEntity(pIndex, dirtyTerm));
                }
                else
                {
                    term.AgregarIndice(pIndex);
                }
            }
        }

        /// <summary>
        /// Incrementa la frecuencia de la palabra dentro de un documento en proceso.
        /// </summary>
        public void IncrementarFrecuencia()
        {
            _tfDoc++;
        }

        /// <summary>
        /// Redefine el metodo hashCode de Object para la clase Palabra.
        /// </summary>
        /// <returns></returns>
        public override int GetHashCode()
        {
            return _cleanTerm == null ? Id : _cleanTerm.GetHashCode();
        }

        /// <summary>
        /// Redefine el metodo equals de Object para la clase Palabra.
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public override bool Equals(Object obj)
        {
            if (obj == null)
            {
                return false;
            }

            if (GetType() != obj.GetType())
            {
                return false;
            }

            var other = (TerminoEntity)obj;

            if ((_cleanTerm == null) ? (other._cleanTerm != null) : !_cleanTerm.Equals(other._cleanTerm))
            {
                return false;
            }

            return true;
        }

        /// <summary>
        /// Redefine el metodo compareTo para la clase Palabra.
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public int CompareTo(object obj)
        {
            var p = (TerminoEntity)obj;

            return _cleanTerm == null ? Id - p.Id : _cleanTerm.CompareTo(p.CleanTerm);
        }

        /// <summary>
        /// Redefinicion del método toString() de Object para la clase Palabra.
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return "Termino: " + _cleanTerm;
        }

        /// <summary>
        /// Calclulo el idf = Log(N/nr) igual que como esta en el doc de Valerio
        /// </summary>
        /// <param name="n"></param>
        /// <returns></returns>
        public double GetIdf(int n)
        {
            var calc = Math.Log10((double)n / (double)_nr);
            return calc;
        }

        #endregion
    }
}
